﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using ChatModel;
using SignalR.Client.Hubs;
using MetroJabbViewModel.Types;

namespace MetroJabbViewModel
{
    internal class ChatFacade
    {
        private const string JoinCommand = "Join";
        private const string AddMessageCommand = "addMessage";
        private const string LeaveCommand = "leave";
        private const string AddUserCommand = "addUser";
        private const string LogOnEvent = "logOn";

        private readonly HubConnection _hubConnection;
        private readonly IHubProxy _chat;
        private readonly IUserDataStore _userDataStore;

        private ObservableCollection<Room> _rooms = new ObservableCollection<Room>();
        private ObservableCollection<User> _users = new ObservableCollection<User>();

        public ChatFacade(string chatServerUrl, string chatHub, IUserDataStore userDataStore)
        {
            if (chatServerUrl == null)
            {
                throw new ArgumentNullException("chatServerUrl");
            }

            if (chatHub == null)
            {
                throw new ArgumentNullException("chatHub");
            }

            if (userDataStore == null)
            {
                throw new ArgumentNullException("userDataStore");
            }

            _userDataStore = userDataStore;
            _hubConnection = new HubConnection(chatServerUrl);
            _chat = _hubConnection.CreateProxy(chatHub);
            _chat.On<dynamic, string>("addMessage", ProcessMessage);
            _chat.On("leave", OnLeave);
            _chat.On("addUser", OnJoin);
            _chat.On<IEnumerable<dynamic>>("logOn", OnLogOn);
            _chat.On("userCreated", UserCreated);
        }

        public event Action Connected = delegate { };

        public event Action Disconnected
        {
            add
            {
                _hubConnection.Closed += value;
            }
            remove
            {
                _hubConnection.Closed -= value;
            }
        }

        public bool IsConnected
        {
            get { return _hubConnection.IsActive; }
        }

        public ObservableCollection<Room> Rooms
        {
            get
            {
                return _rooms;
            }
        }

        public ObservableCollection<User> Users
        {
            get
            {
                return _users;
            }
        }

        public Task Connect()
        {
            if (IsConnected)
            {
                var completionSource = new TaskCompletionSource<object>();
                completionSource.SetResult(null);
                return completionSource.Task;
            }

            return _hubConnection.Start().ContinueWith(
                task =>
                {
                    if (!String.IsNullOrEmpty(_userDataStore.Id))
                    {
                        _chat["id"] = _userDataStore.Id;
                    }
                    
                    _chat.Invoke<bool>(JoinCommand).Wait();
                    // raise Connected event
                    Connected();
                });
        }

        public Task Join(string room)
        {
            if (room == null)
            {
                throw new ArgumentNullException("room");
            }

            return Send("/join " + room);
        }

        public Task Send(string command)
        {
            return _chat.Invoke("send", command);
        }

        //public Task Say(string what, string room)
        //{
        //    try
        //    {
        //        _chat["activeRoom"] = room;
        //        return Say(what);
        //    }
        //    finally
        //    {
        //        _chat["activeRoom"] = null;
        //    }
        //}

        //private Task Say(string what)
        //{
        //    if (what == null)
        //    {
        //        throw new ArgumentNullException("what");
        //    }

        //    return Send(what);
        //}

        public void Disconnect()
        {
            if (IsConnected)
            {
                _hubConnection.Stop();
            }
        }

        private void ProcessMessage(dynamic message, string room)
        {
        }

        private void OnJoin(dynamic user)
        { 
        }

        private void OnLeave(dynamic user) 
        { 
        }

        private void OnLogOn(IEnumerable<dynamic> rooms) 
        {
            string id = (string)_chat["id"];
            if (id != null) 
            {
                _userDataStore.Id = id;
            }
        }

        private void UserCreated()
        {
            string id = (string)_chat["id"];
            if (id != null)
            {
                _userDataStore.Id = id;
            }
        }

        private void VerifyConnected()
        {
            if (!IsConnected)
            {
                throw new InvalidOperationException("A connection to chat server has not been established.");
            }
        }
    }
}